import * as Cesium from 'cesium';
import ModelVS from './ModelVS';
import ModelFS from './ModelFS';
const { DrawCommand, defined, Geometry, GeometryAttribute, ComponentDatatype, DeveloperError,
    PrimitiveType, VertexArray, BufferUsage, SceneMode, Matrix4, BoundingSphere, clone, RenderState, ShadowMode,
    //@ts-ignore
    ClassificationModelDrawCommand, ModelDrawCommand, ModelUtility
} = Cesium;
export default function fixModelDrawCommands() {
    //@ts-ignore
    Cesium.ModelDrawCommands.buildModelDrawCommand = function (
        primitiveRenderResources: any,
        frameState: any,
    ) {
        const shaderBuilder = primitiveRenderResources.shaderBuilder;
        const shaderProgram = createShaderProgram(
            primitiveRenderResources,
            shaderBuilder,
            frameState,
        );
        const model = primitiveRenderResources.model;
        const command =
            primitiveRenderResources.runtimePrimitive.primitive
                .isGaussianSplatPrimitive &&
                (model?.style?.showGaussianSplatting ?? model.showGaussianSplatting)
                ? buildDrawCommandForGaussianSplatPrimitive(
                    primitiveRenderResources,
                    shaderProgram,
                    frameState,
                )
                : buildDrawCommandForModel(
                    primitiveRenderResources,
                    shaderProgram,
                    frameState,
                );

        const hasClassification = defined(model.classificationType);
        if (hasClassification) {
            return new ClassificationModelDrawCommand({
                primitiveRenderResources: primitiveRenderResources,
                command: command,
            });
        }

        return new ModelDrawCommand({
            primitiveRenderResources: primitiveRenderResources,
            command: command,
        });
    };
    function buildDrawCommandForGaussianSplatPrimitive(
        primitiveRenderResources: any,
        shaderProgram: any,
        frameState: any,
    ) {
        const indexBuffer = getIndexBuffer(primitiveRenderResources);
        const model = primitiveRenderResources.model;

        const vertexArray = (() => {
            if (
                !(
                    primitiveRenderResources.runtimePrimitive.primitive
                        ?.hasGaussianSplatTexture ?? false
                )
            ) {
                const splatQuadAttrLocations = {
                    0: 4,
                    1: 1,
                    2: 2,
                    3: 3,
                    screenQuadPosition: 0,
                    splatPosition: 5,
                    splatColor: 6,
                };
                const geometry = new Geometry({
                    attributes: {
                        screenQuadPosition: new GeometryAttribute({
                            componentDatatype: ComponentDatatype.FLOAT,
                            componentsPerAttribute: 2,
                            values: [-2.0, -2.0, 2.0, -2.0, 2.0, 2.0, -2.0, 2.0],
                            //@ts-ignore
                            name: "_SCREEN_QUAD_POS",
                            variableName: "screenQuadPos",
                        }),
                        ...primitiveRenderResources.runtimePrimitive.primitive.attributes,
                        splatPosition: {
                            ...primitiveRenderResources.runtimePrimitive.primitive.attributes.find(
                                (a: any) => a.name === "POSITION",
                            ),
                            name: "_SPLAT_POSITION",
                            variableName: "splatPosition",
                        },
                        splatColor: {
                            ...primitiveRenderResources.runtimePrimitive.primitive.attributes.find(
                                (a: any) => a.name === "COLOR_0",
                            ),
                            name: "_SPLAT_COLOR",
                            variableName: "splatColor",
                        },
                    },
                    indices: indexBuffer,
                    primitiveType: PrimitiveType.TRIANGLE_STRIP,
                });
                //@ts-ignore
                return VertexArray.fromGeometry({
                    context: frameState.context,
                    geometry: geometry,
                    attributeLocations: splatQuadAttrLocations,
                    bufferUsage: BufferUsage.STATIC_DRAW,
                    interleave: false,
                });
            }
            const splatQuadAttrLocations = {
                //    5: 5,
                splatIndex: 5,
            };
            const geometry = new Geometry({
                attributes: {
                    //@ts-ignore
                    screenQuadPosition: new GeometryAttribute({
                        componentDatatype: ComponentDatatype.FLOAT,
                        componentsPerAttribute: 2,
                        values: [-1, -1, 1, -1, 1, 1, -1, 1],
                        //@ts-ignore
                        name: "_SCREEN_QUAD_POS",
                        variableName: "screenQuadPosition",
                    }),
                    splatIndex: {
                        ...primitiveRenderResources.runtimePrimitive.primitive.attributes.find(
                            (a: any) => a.name === "_SPLAT_INDEXES",
                        ),
                    },
                },
                indices: indexBuffer,
                primitiveType: PrimitiveType.TRIANGLE_STRIP,
            });
            //@ts-ignore
            return VertexArray.fromGeometry({
                context: frameState.context,
                geometry: geometry,
                attributeLocations: splatQuadAttrLocations,
                bufferUsage: BufferUsage.STATIC_DRAW,
                interleave: false,
            });
        })();

        model._pipelineResources.push(vertexArray);

        const pass = primitiveRenderResources.alphaOptions.pass;
        const sceneGraph = model.sceneGraph;

        const is3D = frameState.mode === SceneMode.SCENE3D;
        let modelMatrix, boundingSphere;

        if (!is3D && !frameState.scene3DOnly && model._projectTo2D) {
            modelMatrix = Matrix4.multiplyTransformation(
                sceneGraph._computedModelMatrix,
                primitiveRenderResources.runtimeNode.computedTransform,
                new Matrix4(),
            );

            const runtimePrimitive = primitiveRenderResources.runtimePrimitive;
            boundingSphere = runtimePrimitive.boundingSphere2D;
        } else {
            const computedModelMatrix = is3D
                ? sceneGraph._computedModelMatrix
                : sceneGraph._computedModelMatrix2D;

            modelMatrix = Matrix4.multiplyTransformation(
                computedModelMatrix,
                primitiveRenderResources.runtimeNode.computedTransform,
                new Matrix4(),
            );

            boundingSphere = BoundingSphere.transform(
                primitiveRenderResources.boundingSphere,
                modelMatrix,
            );
        }

        // Initialize render state with default values
        let renderState = clone(
            RenderState.fromCache(primitiveRenderResources.renderStateOptions),
            true,
        );

        renderState.cull.face = ModelUtility.getCullFace(
            modelMatrix,
            primitiveRenderResources.primitiveType,
        );
        renderState = RenderState.fromCache(renderState);

        const hasClassification = defined(model.classificationType);
        const castShadows = hasClassification
            ? false
            //@ts-ignore
            : ShadowMode.castShadows(model.shadows);
        const receiveShadows = hasClassification
            ? false
            //@ts-ignore
            : ShadowMode.receiveShadows(model.shadows);
        // Pick IDs are only added to specific draw commands for classification.
        // This behavior is handled by ClassificationModelDrawCommand.
        const pickId = hasClassification
            ? undefined
            : primitiveRenderResources.pickId;

        const command = new DrawCommand({
            boundingVolume: boundingSphere,
            modelMatrix: modelMatrix,
            uniformMap: primitiveRenderResources.uniformMap,
            renderState: renderState,
            vertexArray: vertexArray,
            shaderProgram: shaderProgram,
            cull: model.cull,
            pass: pass,
            count: primitiveRenderResources.count,
            owner: model,
            pickId: pickId,
            //@ts-ignore
            pickMetadataAllowed: true,
            instanceCount: primitiveRenderResources.instanceCount,
            primitiveType: primitiveRenderResources.primitiveType,
            debugShowBoundingVolume: model.debugShowBoundingVolume,
            castShadows: castShadows,
            receiveShadows: receiveShadows,
            orientedBoundingBox:
                primitiveRenderResources.model.content.tile.boundingVolume.boundingVolume,
        });
        return command;
    }
}
//#region 无改动原始代码
function createShaderProgram(
    primitiveRenderResources: any,
    shaderBuilder: any,
    frameState: any,
) {
    shaderBuilder.addVertexLines(ModelVS);
    shaderBuilder.addFragmentLines(ModelFS);

    const model = primitiveRenderResources.model;
    const shaderProgram = shaderBuilder.buildShaderProgram(frameState.context);
    model._pipelineResources.push(shaderProgram);
    return shaderProgram;
}
function getIndexBuffer(primitiveRenderResources: any) {
    const wireframeIndexBuffer = primitiveRenderResources.wireframeIndexBuffer;
    if (defined(wireframeIndexBuffer)) {
        return wireframeIndexBuffer;
    }

    const indices = primitiveRenderResources.indices;
    if (!defined(indices)) {
        return undefined;
    }

    //>>includeStart('debug', pragmas.debug);
    if (!defined(indices.buffer)) {
        throw new DeveloperError("Indices must be provided as a Buffer");
    }
    //>>includeEnd('debug');

    return indices.buffer;
}
function buildDrawCommandForModel(
    primitiveRenderResources: any,
    shaderProgram: any,
    frameState: any,
) {
    const indexBuffer = getIndexBuffer(primitiveRenderResources);
    const model = primitiveRenderResources.model;

    //@ts-ignore
    const vertexArray = new VertexArray({
        context: frameState.context,
        indexBuffer: indexBuffer,
        attributes: primitiveRenderResources.attributes,
    });

    model._pipelineResources.push(vertexArray);

    const pass = primitiveRenderResources.alphaOptions.pass;
    const sceneGraph = model.sceneGraph;

    const is3D = frameState.mode === SceneMode.SCENE3D;
    let modelMatrix, boundingSphere;

    if (!is3D && !frameState.scene3DOnly && model._projectTo2D) {
        modelMatrix = Matrix4.multiplyTransformation(
            sceneGraph._computedModelMatrix,
            primitiveRenderResources.runtimeNode.computedTransform,
            new Matrix4(),
        );

        const runtimePrimitive = primitiveRenderResources.runtimePrimitive;
        boundingSphere = runtimePrimitive.boundingSphere2D;
    } else {
        const computedModelMatrix = is3D
            ? sceneGraph._computedModelMatrix
            : sceneGraph._computedModelMatrix2D;

        modelMatrix = Matrix4.multiplyTransformation(
            computedModelMatrix,
            primitiveRenderResources.runtimeNode.computedTransform,
            new Matrix4(),
        );

        boundingSphere = BoundingSphere.transform(
            primitiveRenderResources.boundingSphere,
            modelMatrix,
        );
    }

    // Initialize render state with default values
    let renderState = clone(
        RenderState.fromCache(primitiveRenderResources.renderStateOptions),
        true,
    );

    renderState.cull.face = ModelUtility.getCullFace(
        modelMatrix,
        primitiveRenderResources.primitiveType,
    );
    renderState = RenderState.fromCache(renderState);

    const hasClassification = defined(model.classificationType);
    const castShadows = hasClassification
        ? false
        //@ts-ignore
        : ShadowMode.castShadows(model.shadows);
    const receiveShadows = hasClassification
        ? false
        //@ts-ignore
        : ShadowMode.receiveShadows(model.shadows);
    // Pick IDs are only added to specific draw commands for classification.
    // This behavior is handled by ClassificationModelDrawCommand.
    const pickId = hasClassification
        ? undefined
        : primitiveRenderResources.pickId;

    const command = new DrawCommand({
        boundingVolume: boundingSphere,
        modelMatrix: modelMatrix,
        uniformMap: primitiveRenderResources.uniformMap,
        renderState: renderState,
        vertexArray: vertexArray,
        shaderProgram: shaderProgram,
        cull: model.cull,
        pass: pass,
        count: primitiveRenderResources.count,
        owner: model,
        pickId: pickId,
        //@ts-ignore
        pickMetadataAllowed: true,
        instanceCount: primitiveRenderResources.instanceCount,
        primitiveType: primitiveRenderResources.primitiveType,
        debugShowBoundingVolume: model.debugShowBoundingVolume,
        castShadows: castShadows,
        receiveShadows: receiveShadows,
    });
    return command;
}
//#endregion